/* Copyright (C) 2007-2010 STS Soft

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

using System.Diagnostics;

namespace STSdb.Files
{
    public partial class FileSystem
    {
        private ushort[] path = new ushort[Node.MAX_DEPTH];

        internal RawFile RecycleFile;

        /// <summary>
        /// seeks for first deleted and not fully recycled file
        /// </summary>
        private RawFile FindRecycleFile(ulong fromHandle)
        {
            lock (SyncRoot)
            {
                ulong handle;
                while (fromHandle < HandleCount && BinaryGarbageFile.FindFirstBit(fromHandle, HandleCount - fromHandle, true, out handle))
                {
                    FileInfo fi = GetFileInfo(handle);
                    if (fi.SectorID != Node.INVALID_SECTOR_ID)
                        return new RawFile(this, handle, fi.SectorID, fi.Length);

                    fromHandle = handle + 1;
                }

                return null;
            }
        }

        internal ulong GetFreeSectorID()
        {
            lock (SyncRoot)
            {
                ulong sectorID = sectorCount;
                if (RecycleFile == null)
                {
                    sectorCount++;
                    return sectorID;
                }

                sectorID = GetFreeSectorID(RecycleFile, RecycleFile.Root, 0);
                Debug.Assert(sectorID != Node.INVALID_SECTOR_ID);

                Volume.Write(sectorID * Sector.SIZE_64, Sector.Zero64, 0, (int)Sector.SIZE_64);

                if (sectorID == RecycleFile.RootSectorID)
                {
                    //mark the file as a fully recycled
                    SetFileInfo(RecycleFile.Handle, new FileInfo(0, 0));

                    //put the next file for recycling
                    //RecycleFile = FindRecycleFile(RecycleFile.Handle + 1);
                    RecycleFile = FindRecycleFile(0);

                    for (int i = 0; i < path.Length; i++)
                        path[i] = 0;
                }

                return sectorID;
            }
        }

        private ulong GetFreeSectorID(RawFile file, Node node, int level)
        {
            for (; path[level] < Node.BRANCH_LENGTH; path[level]++)
            {
                if (level < file.Depth - 1)
                {
                    Node currNode = node[path[level], false];
                    if (currNode == null)
                        continue;

                    ulong sectorID = GetFreeSectorID(file, currNode, level + 1);
                    if (sectorID == currNode.SectorID)
                        node.SetPointer(path[level], Node.INVALID_SECTOR_ID);

                    return sectorID;
                }
                else
                {
                    ulong sectorID = node.GetPointer(path[level]);
                    if (sectorID == Node.INVALID_SECTOR_ID)
                        continue;

                    node.SetPointer(path[level], Node.INVALID_SECTOR_ID);

                    return sectorID;
                }
            }

            lock (file.FileSystem.Cache)
                file.FileSystem.Cache.Exclude(node.SectorID);

            path[level] = 0;
            return node.SectorID;
        }
    }
}
